package jp.co.nes.awssupport.common.base;

import org.apache.commons.lang3.StringUtils;
import org.kohsuke.args4j.CmdLineParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * アプリケーション(コンソールアプリケーション)を作成する際に使用する抽象クラスです。
 */
public abstract class AbstractClient {

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(AbstractClient.class);

    /**
     * デフォルトコンストラクタです。
     */
    protected AbstractClient() {
    }

    /**
     * 処理を実行します。
     *
     * @param args コマンドライン引数
     */
    public void execute(String[] args) {
        long start = System.currentTimeMillis();
        String classSimpleName = null;
        CmdLineParser parser = null;
        boolean isSuccess = false;
        Throwable throwable = null;
        try {
            classSimpleName = getClass().getSimpleName();
            if (StringUtils.isEmpty(classSimpleName)) {
                // 無名クラスだった場合、getSimpleName()の戻り値が空なので、getName()の値を設定する。
                classSimpleName = getClass().getName();
            }
            logger.info("*** " + classSimpleName + " start ***");

            // コマンドライン引数の解析
            parser = new CmdLineParser(this);
            parser.parseArgument(args);
            debugArgument();

            checkTimezone();
            execute();
            isSuccess = true;
        } catch (Throwable e) {
            throwable = e;
            failed(e);
        } finally {
            finish(isSuccess, throwable);
            if (isSuccess) {
                long time = System.currentTimeMillis() - start;
                logger.info("*** " + classSimpleName + " end(time=" + time + ") ***");
            }
        }
    }

    /**
     * タイムゾーンがUTCかどうかチェックします。
     */
    protected void checkTimezone() {
        String timezone = System.getProperty("user.timezone");
        if (StringUtils.isEmpty(timezone) || !timezone.toUpperCase().equals("UTC")) {
            throw new CommonException("timezone is wrong(timezone=" + timezone + ")");
        }
    }

    /**
     * 処理を実行します。
     */
    protected abstract void execute();

    /**
     * 終了処理です。
     *
     * @param isSuccess true: 処理成功, false: 処理失敗
     * @param throwable 発生した例外
     */
    protected abstract void finish(boolean isSuccess, Throwable throwable);

    /**
     * 処理で例外が発生した際に呼び出される処理です。
     *
     * @param throwable 発生した例外
     */
    protected void failed(Throwable throwable) {
        logger.error(throwable.getMessage(), throwable);
        if (throwable instanceof RuntimeException) {
            throw (RuntimeException) throwable;
        } else if (throwable instanceof Error) {
            throw (Error) throwable;
        }
        throw new CommonException(throwable);
    }

    /**
     * コマンドライン引数の解析結果をデバッグログに出力します。
     */
    protected abstract void debugArgument();
}
